package com.jt.auth.config;

import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * Oauth2 是什么?
 * Oauth2 是一种规范(协议),此规范中定义了完成用户身份认证和授权的一种方式.
 * 例如:基于用户和密码进行认证,基于第三方(微信,QQ)应用进行认证,...

 * Oauth2规范中定义哪些核心对象?
 * 1)资源对象(数据库或文件中的数据)
 * 2)资源服务器对象(有你要访问的资源)
 * 3)认证管理器对象(负责用户身份的认证,完整认证后方可访问资源)
 * 4)认证客户端对象(password,微信,....)
 * 5)用户对象(需要认证的对象)
 *
 * OAuth2规范中如何对用户身份进行认证?
 * 1)提供负责认证的地址?(用户去哪里认证,例如结婚去民政局)
 * 2)告诉用户携带什么资料去认证?(携带的信息,例如身份证,户口本,...)
 * 3)提供完成用户身份认证的对象?(AuthenticationManager)
 * 4)认证成功后颁发什么令牌?(token)
 * 接下来的配置中就要完成如上几个内容.
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer //表示启动授权服务
public class Oauth2Config
        extends AuthorizationServerConfigurerAdapter {
    //@Autowired
    private PasswordEncoder passwordEncoder;
    //@Autowired
    private AuthenticationManager authenticationManager;
    //@Autowired
    private TokenStore tokenStore;
    /**
     * 公开认证的地址?(这个地址oauth2中定义好了,
     * 客户端将认证信息提交到这个地址)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(
            AuthorizationServerSecurityConfigurer security)
            throws Exception {
        //super.configure(security);
        security//公开认证地址/oauth/token(所有请求无需认证就可以访问这个地址)
                .tokenKeyAccess("permitAll()")
                //公开检查令牌的值/oauth/check_token(所有请求无需认证就可以访问这个地址)
                .checkTokenAccess("permitAll()")
                //允许form表单认证
                .allowFormAuthenticationForClients();
    }
    /**
     * 定义客户端认证时,需要携带的信息.
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //super.configure(clients);
        clients.inMemory()
                //定义客户端标识
               .withClient("gateway-client")//这个名字是自己定义
                //定义客户端需要携带的秘钥
               .secret(passwordEncoder.encode("123456"))
               //定义允许客户端认证的方式(基于密码方式和刷新令牌)
               .authorizedGrantTypes("password","refresh_token")
               //指定作用域(满足如上所有条件的client都可以进行认证)
               .scopes("all");
    }

    /**
     * 定义完成用户身份认证的对象
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //super.configure(endpoints);
        //设置认证管理器(这个对象由Spring Security提供)
        endpoints.authenticationManager(authenticationManager);
        //设置令牌生成机制以及令牌特性(改变默认令牌创建方式以及其特性)
        endpoints.tokenServices(tokenServices());
        //定义允许客户端的请求方式
        //endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        //创建AuthorizationServerTokenServices类型的对象
        DefaultTokenServices tokenServices=
                new DefaultTokenServices();
        //设置令牌存储方式
        tokenServices.setTokenStore(tokenStore);
        //设置访问令牌的有效时长
        tokenServices.setAccessTokenValiditySeconds(3600);
        //设置刷新令牌以及有效时长(默认没有刷新令牌)
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setRefreshTokenValiditySeconds(3600*24);
        return tokenServices;
    }


}
